﻿using NLog;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Com.Lancetinc.Evs.Helpers;

namespace Com.Lancetinc.Evs.Communications
{
    public class ConnectionAliveKeeper
    {
        private readonly Logger logger;
        private readonly IConnection connection;
        private readonly IInstrumentManager instManager;
        private volatile bool searching = false;
        private readonly InstrumentStateQueryer stateQueryer;

        public string[] IgnorePortNames { get; set; } = null;

        public ConnectionAliveKeeper(IInstrumentManager instManager, IConnection connection, Logger logger)
        {
            this.instManager = instManager;
            this.connection = connection;
            this.logger = logger;
            this.stateQueryer = new InstrumentStateQueryer(instManager);

            try
            {
                var file = Path.Combine(DirUtil.ExeDirectory, "IgnorePortNames.txt");
                if (File.Exists(file))
                {
                    IgnorePortNames = File.ReadAllLines(file)
                        .Select(x => x.Trim())
                        .Where(x => !string.IsNullOrEmpty(x)).ToArray();
                    if (IgnorePortNames.Length > 0)
                    {
                        logger.Warn("IgnorePortNames:" + string.Join(",", IgnorePortNames));
                    }
                }
            }
            catch (Exception e)
            {
                logger.Error(e);
            }
        }

        public void Tick()
        {
            if (!instManager.IsOnline)
            {
                stateQueryer.ClearTickTimes();
                if (!this.searching)
                {
                    Task.Factory.StartNew(Search);
                }
            }
            else
            {
                stateQueryer.QueryIfNeed();
            }
        }

        private void Search()
        {
            searching = true;
            try
            {
                var availablePortNames = SerialConnection.GetPortNames(connection.PortName);
                if (IgnorePortNames is { Length: > 0 })
                    availablePortNames = availablePortNames.Except(IgnorePortNames).ToArray();

                int index = 0;
                while (instManager.IsRunning && index < availablePortNames.Length && !instManager.IsOnline)
                {
                    try
                    {
                        connection.Connect(availablePortNames[index]);
                        connection.Send(PacketHelper.CreateConnect());
                        WaitAlive(2000);
                        if (!instManager.IsOnline)
                        {
                            connection.Close();
                        }
                    }
                    catch (Exception ex)
                    {
                        logger.Debug(ex.GetType().Name + ":" + ex.Message);
                        connection.Close();
                    }
                    index++;
                }
            }
            finally
            {
                searching = false;
            }
        }

        private void WaitAlive(int millseconds)
        {
            while (millseconds > 0 && instManager.IsRunning && !instManager.IsOnline)
            {
                System.Threading.Thread.Sleep(5);
                millseconds -= 5;
            }
        }
    }
}
